package com.mcx.modules.common;

import org.apache.commons.lang.NullArgumentException;

public class BitConverter {
    /**
     * Indicates the byte order ("endianess") in which data is stored in this computer architecture.
     */
    public static Boolean IsLittleEndian = true;

    /**
     * Returns the specified Boolean value as an array of bytes.
     * @param value A Boolean value.
     * @return An array of bytes with length 1.
     */
    public static byte[] GetBytes(Boolean value)
    {
        return new byte[] {value ? (byte) 1 : (byte) 0};
    }

    /**
     * Returns the specified Unicode character value as an array of bytes.
     * @param value A character to convert.
     * @return An array of bytes with length 2.
     */
    public static byte[] GetBytes(char value)
    {
        return GetBytes((short) value);
    }

    /**
     * Returns the specified 16-bit signed integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 2.
     */
    public static byte[] GetBytes(short value)
    {
        if (IsLittleEndian)
            return new byte[] {
                    (byte) ((value) & 0xFF),
                    (byte) (value >> 8 & 0xFF)
        };
        else
        return new byte[] {
                (byte) ((value >> 8) & 0xFF),
                (byte) (value & 0xFF)
        };
    }

    /**
     * Returns the specified 32-bit signed integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 4.
     */
    public static byte[] GetBytes(int value)
    {
        if (IsLittleEndian)
            return new byte[] {
                    (byte) ((value) & 0xFF),
                    (byte) ((value >> 8) & 0xFF),
                    (byte) ((value >> 16) & 0xFF),
                    (byte) (value >> 24 & 0xFF)
            };
        else
            return new byte[] {
                    (byte) ((value >> 24) & 0xFF),
                    (byte) ((value >> 16) & 0xFF),
                    (byte) ((value >> 8) & 0xFF),
                    (byte) (value & 0xFF)
            };
    }

    /**
     * Returns the specified 64-bit signed integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 8.
     */
    public static byte[] GetBytes(long value){
        if (IsLittleEndian)
            return new byte[] {
                    (byte) ((value) & 0xFF),
                    (byte) ((value >> 8) & 0xFF),
                    (byte) ((value >> 16) & 0xFF),
                    (byte) ((value >> 24) & 0xFF),
                    (byte) ((value >> 32) & 0xFF),
                    (byte) ((value >> 50) & 0xFF),
                    (byte) ((value >> 48) & 0xFF),
                    (byte) (value >> 56 & 0xFF)
            };
        else
            return new byte[] {
                    (byte) ((value >> 56) & 0xFF),
                    (byte) ((value >> 48) & 0xFF),
                    (byte) ((value >> 40) & 0xFF),
                    (byte) ((value >> 32) & 0xFF),
                    (byte) ((value >> 24) & 0xFF),
                    (byte) ((value >> 16) & 0xFF),
                    (byte) ((value >> 8) & 0xFF),
                    (byte) (value & 0xFF)
            };
    }

    /**
     * Returns the specified 16-bit unsigned integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 2.
     */
    public static byte[] GetBytes(ushort value) {
        return BitConverter.GetBytes(value.getValue());
    }

    /**
     * Returns the specified 32-bit unsigned integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 4.
     */
    public static byte[] GetBytes(uint value) {
        return BitConverter.GetBytes(value.getValue());
    }

    /**
     * Returns the specified 64-bit unsigned integer value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 8.
     */
    public static byte[] GetBytes(ulong value) {
        return BitConverter.GetBytes(value.getValue());
    }

    /**
     * Returns the specified single-precision floating point value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 4.
     */
    public static byte[] GetBytes(float value) {
        return BitConverter.GetBytes((int)value);
    }

    /**
     * Returns the specified double-precision floating point value as an array of bytes.
     * @param value The number to convert.
     * @return An array of bytes with length 8.
     */
    public static byte[] GetBytes(double value) {
        return BitConverter.GetBytes((long)value);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static char ToChar(byte[] value, int startIndex) {
        return  (char) BitConverter.ToInt16(value, startIndex);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static short ToInt16(byte[] value, int startIndex)
    {
        if (value == null)
            throw new NullPointerException("value is null");
        if ((long) startIndex >= (long) value.length)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        if (startIndex > value.length - 2)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        return IsLittleEndian ? (short)((value[startIndex] & 0xFF) | (value[startIndex+1] & 0xFF) << 8) : (short)((value[startIndex] & 0xFF) << 8 | (value[startIndex+1] & 0xFF));
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static int ToInt32(byte[] value, int startIndex)
    {
        if (value == null)
            throw new NullPointerException("value is null");
        if ((long) startIndex >= (long) value.length)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        if (startIndex > value.length - 4)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        return IsLittleEndian ? ((value[startIndex] & 0xFF) | (value[startIndex+1] & 0xFF) << 8 | (value[startIndex+2] & 0xFF) << 16 | (value[startIndex+3] & 0xFF) << 24) : (short)((value[startIndex] & 0xFF) << 24 |(value[startIndex+1] & 0xFF) << 16 |(value[startIndex+2] & 0xFF) << 8 | (value[startIndex+3] & 0xFF));
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static long ToInt64(byte[] value, int startIndex)
    {
        if (value == null)
            throw new NullPointerException("value is null");
        if ((long) startIndex >= (long) value.length)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        if (startIndex > value.length - 4)
            throw new ArrayIndexOutOfBoundsException("startIndex out");
        if (IsLittleEndian)
            return (long)((value[startIndex] & 0xFF) | (value[startIndex+1] & 0xFF) << 8 | (value[startIndex+2] & 0xFF) << 16 | (value[startIndex+3] & 0xFF) << 24) | (long)((value[startIndex+4] & 0xFF) | (value[startIndex+5] & 0xFF) << 8 | (value[startIndex+6] & 0xFF) << 16 | (value[startIndex+7] & 0xFF) << 24) << 32;
        else
            return (long)((value[startIndex+4] & 0xFF) << 24 | (value[startIndex+5] & 0xFF) << 16 | (value[startIndex+6] & 0xFF) << 8 | (value[startIndex+7] & 0xFF)) | (long)((value[startIndex] & 0xFF) << 24 | (value[startIndex+1] & 0xFF) << 16 | (value[startIndex+2] & 0xFF) << 8 | (value[startIndex+3] & 0xFF)) << 32;
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static short ToUInt16(byte[] value, int startIndex) {
        return  BitConverter.ToInt16(value, startIndex);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static int ToUInt32(byte[] value, int startIndex) {
        return BitConverter.ToInt32(value, startIndex);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static long ToUInt64(byte[] value, int startIndex) {
        return BitConverter.ToInt64(value, startIndex);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static float ToSingle(byte[] value, int startIndex) {
        return  Float.intBitsToFloat(BitConverter.ToInt32(value, startIndex));
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static double ToDouble(byte[] value, int startIndex) {
        return (double)BitConverter.ToInt64(value, startIndex);
    }

    private static char GetHexValue(int i) {
        return i< 10 ? (char) (i + 48) : (char) (i - 10 + 65);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @param length The number of array elements in @param value to convert.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static String ToString(byte[] value, int startIndex, int length)
    {
        int num1;if (value != null) num1 = value.length;else throw new NullArgumentException("byteArray");
        if (startIndex < 0 || startIndex >= num1 && startIndex > 0)
            throw new IndexOutOfBoundsException("ArgumentOutOfRange_StartIndex");
        int num2 = length;
        if (num2 < 0)
            throw new IndexOutOfBoundsException("ArgumentOutOfRange_GenericPositive");
        if (startIndex > num1 - num2)
            throw new RuntimeException("Arg_ArrayPlusOffTooSmall");
        if (num2 == 0)
            return "";
        char[] chArray = new char[num2 * 3];
        int num3 = startIndex;
        for (int index = 0; index < num2 * 3; index += 3)
        {
            byte num4 = value[num3++];
            chArray[index] = BitConverter.GetHexValue((int) num4 / 16);
            chArray[index + 1] = BitConverter.GetHexValue((int) num4 % 16);
            chArray[index + 2] = '-';
        }
        return new String(chArray, 0, chArray.length - 1);
    }

    /**
     * Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string representation.
     * @param value An array of bytes.
     * @return  A <see cref="T:System.String" /> of hexadecimal pairs separated by hyphens, where each pair represents the corresponding element in <paramref name="value" />; for example, "7F-2C-4A".
     */
    public static String ToString(byte[] value) {
        if (value != null) return BitConverter.ToString(value, 0, value.length); else throw new NullArgumentException("value is null");
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static String ToString(byte[] value, int startIndex)
    {
        if (value == null)
            throw new NullArgumentException("value is null");
        return BitConverter.ToString(value, startIndex, value.length - startIndex);
    }

    /**
     * Returns a Unicode character converted from two bytes at a specified position in a byte array.
     * @param value An array of bytes.
     * @param startIndex The starting position within @param value.
     * @return A character formed by two bytes beginning at @param startIndex .
     */
    public static Boolean ToBoolean(byte[] value, int startIndex)
    {
        if (value == null)
            throw new NullArgumentException("value is null");
        if (startIndex < 0)
            throw new IndexOutOfBoundsException("ArgumentOutOfRange_NeedNonNegNum");
        if (startIndex > value.length - 1)
            throw new IndexOutOfBoundsException("ArgumentOutOfRange_Index");
        return value[startIndex] != (byte) 0;
    }

    /**
     * Converts the specified double-precision floating point number to a 64-bit signed integer.
     * @param value The number to convert.
     * @return A 64-bit signed integer whose value is equivalent to <paramref name="value" />.
     */
    public static long DoubleToInt64Bits(double value) {
        return (long) value;
    }

    /**
     * Converts the specified 64-bit signed integer to a double-precision floating point number.
     * @param value The number to convert.
     * @return A double-precision floating point number whose value is equivalent to <paramref name="value" />.
     */
    public static double Int64BitsToDouble(long value) {
        return (double) value;
    }
}

class ushort {

    public final static int SIZE = 16;

    private final static long MIN = 0L;
    private final static long MAX = (1L << SIZE) - 1;

    public final static ushort MIN_VALUE = new ushort(MIN);
    public final static ushort MAX_VALUE = new ushort(MAX);

    private Long value;

    public ushort() {
        this(0L);
    }

    public ushort(long value) {
        setValue(value);
    }

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        check(value);
        this.value = value & MAX;
    }

    private void check(long value) {
        if(value < MIN || value > MAX) {
            throw new IllegalArgumentException(value + " 值必须在 " + MIN + " 到 " + MAX + " 之间");
        }
    }

    @Override
    public int hashCode() {
        return ((Long)value).hashCode();
    }

    public String toString() {
        return String.valueOf(value);
    }

    public int compareTo(ushort obj) {
        if(obj == null) {
            return -1;
        }
        if(this.value == obj.value) {
            return 0;
        }
        return this.value > obj.value ? 1 : 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final ushort other = (ushort) obj;
        if (value != other.value)
            return false;
        return true;
    }
}

class uint {

    public final static int SIZE = 32;

    private final static long MIN = 0L;
    private final static long MAX = (1L << SIZE) - 1;

    public final static uint MIN_VALUE = new uint(MIN);
    public final static uint MAX_VALUE = new uint(MAX);

    private Long value;

    public uint() {
        this(0L);
    }

    public uint(long value) {
        setValue(value);
    }

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        check(value);
        this.value = value & MAX;
    }

    private void check(long value) {
        if(value < MIN || value > MAX) {
            throw new IllegalArgumentException(value + " 值必须在 " + MIN + " 到 " + MAX + " 之间");
        }
    }

    @Override
    public int hashCode() {
        return ((Long)value).hashCode();
    }

    public String toString() {
        return String.valueOf(value);
    }

    public int compareTo(uint obj) {
        if(obj == null) {
            return -1;
        }
        if(this.value == obj.value) {
            return 0;
        }
        return this.value > obj.value ? 1 : 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final uint other = (uint) obj;
        if (value != other.value)
            return false;
        return true;
    }
}

class ulong {

    public final static int SIZE = 64;

    private final static long MIN = 0L;
    private final static long MAX = (1L << SIZE) - 1;

    public final static ulong MIN_VALUE = new ulong(MIN);
    public final static ulong MAX_VALUE = new ulong(MAX);

    private Long value;

    public ulong() {
        this(0L);
    }

    public ulong(long value) {
        setValue(value);
    }

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        check(value);
        this.value = value & MAX;
    }

    private void check(long value) {
        if(value < MIN || value > MAX) {
            throw new IllegalArgumentException(value + " 值必须在 " + MIN + " 到 " + MAX + " 之间");
        }
    }

    @Override
    public int hashCode() {
        return ((Long)value).hashCode();
    }

    public String toString() {
        return String.valueOf(value);
    }

    public int compareTo(ulong obj) {
        if(obj == null) {
            return -1;
        }
        if(this.value == obj.value) {
            return 0;
        }
        return this.value > obj.value ? 1 : 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final ulong other = (ulong) obj;
        if (value != other.value)
            return false;
        return true;
    }
}
